package org.finjic.annotated.scanner;

import org.finjic.annotated.annotations.Component;
import org.finjic.exception.DuplicateBeanIdException;
import org.finjic.entity.BeanDefinition;
import org.finjic.utils.ReflectionUtils;
import org.finjic.utils.StringUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * User: Taras
 * Date: 18.11.12
 * Time: 15:29
 */
public class ComponentClassScanner {
    private String packageName;

    public ComponentClassScanner(String packageName) {
        this.packageName = packageName;
    }

    public Set<BeanDefinition> getComponentsFromPackage() {
        Set<Class<?>> componentClasses = getClassesWithComponentAnnotation();
        if(componentClasses.isEmpty()){
            return Collections.emptySet();
        }
        Set<BeanDefinition> beanDefinitions = new HashSet<BeanDefinition>();
        Set<String> ids = new HashSet<String>();
        for(Class clazz : componentClasses){
            BeanDefinition beanDefinition = processClass(clazz);
            if(isDuplicateId(beanDefinition.getId(), ids)){
                throw new DuplicateBeanIdException(beanDefinition.getId());
            }
            beanDefinitions.add(beanDefinition);
        }
        return beanDefinitions;
    }

    private Set<Class<?>> getClassesWithComponentAnnotation() {
        return ReflectionUtils.getTypesAnnotatedWith(packageName, Component.class);
    }

    private String getBeanName(Class clazz) {
        String beanName = getBeanNameFromAnnotation(clazz);
        if (beanName.isEmpty()) {
            beanName = getBeanNameFromClassName(clazz);
        }
        return beanName;
    }

    @SuppressWarnings("unchecked")
    private String getBeanNameFromAnnotation(Class clazz) {
        Component annotation = (Component) clazz.getAnnotation(Component.class);
        return annotation.value();
    }

    private String getBeanNameFromClassName(Class clazz) {
        return StringUtils.uncapitalize(clazz.getSimpleName());
    }

    private BeanDefinition processClass(Class clazz){
        String beanId = getBeanName(clazz);
        BeanDefinition beanDefinition  = new BeanDefinition(beanId, clazz.getCanonicalName());

        return beanDefinition;
    }

    private boolean isDuplicateId(String beanId, Set<String> ids){
        return ids.contains(beanId);
    }
}
